package com.qimu.saiyou.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qimu.saiyou.common.ErrorCode;
import com.qimu.saiyou.constant.CommonConstant;
import com.qimu.saiyou.exception.BusinessException;
import com.qimu.saiyou.exception.ThrowUtils;
import com.qimu.saiyou.mapper.TeamMapper;
import com.qimu.saiyou.model.dto.team.TeamJoinRequest;
import com.qimu.saiyou.model.dto.team.TeamKickOutRequest;
import com.qimu.saiyou.model.dto.team.TeamQueryRequest;
import com.qimu.saiyou.model.dto.team.TeamTransferOfIdentityRequest;
import com.qimu.saiyou.model.entity.Competition;
import com.qimu.saiyou.model.entity.Team;
import com.qimu.saiyou.model.entity.User;
import com.qimu.saiyou.model.enums.TeamRoleEnum;
import com.qimu.saiyou.model.vo.TeamVO;
import com.qimu.saiyou.model.vo.UserVO;
import com.qimu.saiyou.service.CompetitionService;
import com.qimu.saiyou.service.TeamService;
import com.qimu.saiyou.service.UserService;
import com.qimu.saiyou.utils.RedissonLockUtil;
import com.qimu.saiyou.utils.SqlUtils;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.similarity.LevenshteinDistance;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.qimu.saiyou.constant.TeamConstant.PRIVATE_TEAM_STATUS;
import static com.qimu.saiyou.service.impl.UserServiceImpl.SALT;

/**
 * 帖子服务实现
 *
 *@author: FeiXiang
 * 
 */
@Service
@Slf4j
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private UserService userService;
    @Resource
    private CompetitionService competitionService;

    @Resource
    private RedissonLockUtil redissonLockUtil;

    @Override
    public void validTeam(Team team, boolean add) {
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = team.getId();
        String teamName = team.getTeamName();
        String teamAvatarUrl = team.getTeamAvatarUrl();
        String teamPassword = team.getTeamPassword();
        String tags = team.getTags();
        String wantToTags = team.getWantToTags();
        Integer maxNum = team.getMaxNum();
        Long teamLeaderId = team.getTeamLeaderId();
        Long teamCaptainId = team.getTeamCaptainId();
        String usersId = team.getUsersId();
        Integer teamStatus = team.getTeamStatus();
        Integer isDelete = team.getIsDelete();
        Date createTime = team.getCreateTime();
        String announce = team.getAnnounce();
        Date updateTime = team.getUpdateTime();

        // 创建时，参数不能为空
        if (add) {
            ThrowUtils.throwIf(StringUtils.isAnyBlank(teamName, tags), ErrorCode.PARAMS_ERROR);
        }
        // 有参数则校验
        if (StringUtils.isNotBlank(teamName) && teamName.length() > 14) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队名过长");
        }
        if (StringUtils.isNotBlank(announce) && announce.length() > 40) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "公告内容过长");
        }
        if (ObjectUtils.isNotEmpty(maxNum) && maxNum < 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍最大人数不能少于1人");
        }
        if (ObjectUtils.isNotEmpty(maxNum) && maxNum > 7) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍最大人数不能多于7人");
        }
        if (ObjectUtils.anyNotNull(teamStatus) && teamStatus == PRIVATE_TEAM_STATUS && StringUtils.isBlank(teamPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密队伍需设置密码");
        }
        if (ObjectUtils.isNotEmpty(teamStatus) && teamStatus == PRIVATE_TEAM_STATUS && StringUtils.isNotBlank(teamPassword)) {
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + teamPassword).getBytes(StandardCharsets.UTF_8));
            team.setTeamPassword(encryptPassword);
        }

    }

    /**
     * 获取查询包装类
     *
     * @param teamQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Team> getQueryWrapper(TeamQueryRequest teamQueryRequest) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        if (teamQueryRequest == null) {
            return queryWrapper;
        }

        String searchText = teamQueryRequest.getSearchText();
        String sortField = teamQueryRequest.getSortField();
        String sortOrder = teamQueryRequest.getSortOrder();
        Long id = teamQueryRequest.getId();
        List<String> tagList = teamQueryRequest.getTags();
        Long leaderId = teamQueryRequest.getTeamLeaderId();
        Long captainId = teamQueryRequest.getTeamCaptainId();
        Integer status = teamQueryRequest.getStatus();
        Long userId = teamQueryRequest.getUserId();

        // 拼接查询条件
        if (StringUtils.isNotBlank(searchText)) {
            queryWrapper.and(qw -> qw.like("teamName", searchText).or()
                    .like("announce", searchText).or()
                    .like("tags", searchText).or()
                    .like("wantToTags", searchText));
        }

        if (CollUtil.isNotEmpty(tagList)) {
            for (String tag : tagList) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(leaderId), "teamLeaderId", leaderId);
        queryWrapper.like(ObjectUtils.isNotEmpty(userId), "usersId", userId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(captainId), "teamCaptainId", captainId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(status), "status", status);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    @Override
    public TeamVO getTeamVO(Team team, HttpServletRequest request) {
        return getTeamVO(team);
    }

    public TeamVO getTeamVO(Team team) {
        if (team == null) {
            return new TeamVO();
        }
        TeamVO teamVO = TeamVO.objToVo(team);
        long teamId = team.getId();
        // 1. 关联查询带队老师、队长信息
        Long leaderId = team.getTeamLeaderId();
        User user = null;
        if (leaderId != null && leaderId > 0) {
            user = userService.getById(leaderId);
        }
        UserVO leaderUserVo = userService.getUserVO(user);
        teamVO.setTeamLeader(leaderUserVo);
        Long captainId = team.getTeamCaptainId();
        if (captainId != null && captainId > 0) {
            user = userService.getById(captainId);
        }
        UserVO captainUserVO = userService.getUserVO(user);
        teamVO.setTeamCaptain(captainUserVO);
        String usersId = team.getUsersId();
        List<Long> teamUserIdList = JSONUtil.toList(usersId, Long.class);
        List<User> userList = userService.listByIds(teamUserIdList);
        teamVO.setTeamUserList(userService.getUserVO(userList));
        return teamVO;
    }

    /**
     * 获取队伍列表
     *
     * @param teamPage 团队页面
     * @param request  要求
     * @return {@link Page}<{@link TeamVO}>
     */
    @Override
    public Page<TeamVO> getTeamVOPage(Page<Team> teamPage, HttpServletRequest request) {
        List<Team> teamList = teamPage.getRecords();
        Page<TeamVO> teamVoPage = new Page<>(teamPage.getCurrent(), teamPage.getSize(), teamPage.getTotal());
        if (CollUtil.isEmpty(teamList)) {
            return teamVoPage;
        }
        // 填充信息
        List<TeamVO> teamVOList = teamList.stream().map(this::getTeamVO).collect(Collectors.toList());
        User loginUser = userService.getLoginUserPermitNull(request);
        if (loginUser != null) {
            String tags = loginUser.getTags();
            List<Pair<TeamVO, Integer>> distanceTeamList = new ArrayList<>();
            LevenshteinDistance levenshteinDistance = new LevenshteinDistance();
            teamVOList.forEach(teamVO -> {
                // 计算两个字符串之间的编辑距离
                int distance = levenshteinDistance.apply(tags, teamVO.getTagList().toString());
                distanceTeamList.add(new Pair<>(teamVO, distance));
            });
            // 按编辑距离由小到大排序
            teamVOList = distanceTeamList.stream()
                    .sorted(Comparator.comparingInt(Pair::getValue))
                    .map(Pair::getKey)
                    .collect(Collectors.toList());

            int halfSize = teamVOList.size() / 2;
            List<TeamVO> firstHalf = teamVOList.subList(0, halfSize);
            List<TeamVO> secondHalf = teamVOList.subList(halfSize, teamVOList.size());
            // 打乱前一半元素的顺序
            Collections.shuffle(firstHalf);
            Collections.shuffle(secondHalf);
            // 合并打乱后的前一半和后一半元素
            List<TeamVO> shuffledTeamVOList = new ArrayList<>(firstHalf);
            shuffledTeamVOList.addAll(secondHalf);
            teamVOList = shuffledTeamVOList;
        }
        teamVoPage.setRecords(teamVOList);
        return teamVoPage;
    }

    /**
     * 团队身份转移
     *
     * @param teamTransferOfIdentityRequest 团队身份转移请求
     * @param request                       要求
     * @return boolean
     */
    @Override
    public boolean teamAdminTransferOfIdentity(TeamTransferOfIdentityRequest teamTransferOfIdentityRequest, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        Long teamId = teamTransferOfIdentityRequest.getTeamId();
        String type = teamTransferOfIdentityRequest.getType();
        String toUserId = teamTransferOfIdentityRequest.getToUserId();
        boolean admin = userService.isAdmin(loginUser);
        ThrowUtils.throwIf(loginUser.getId().equals(Long.valueOf(toUserId)) && !admin, ErrorCode.OPERATION_ERROR, "不能转移给自己");
        Team team = this.getById(teamId);
        ThrowUtils.throwIf(team == null, ErrorCode.OPERATION_ERROR, "队伍不存在");
        if (!admin && !team.getTeamLeaderId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        ThrowUtils.throwIf(!team.getUsersId().contains(toUserId), ErrorCode.OPERATION_ERROR, "该用户不在队伍");
        if (!TeamRoleEnum.getValues().contains(type)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有该职位");
        }
        if (type.equals(TeamRoleEnum.CAPTAIN.getValue())) {
            team.setTeamCaptainId(Long.valueOf(toUserId));
        }
        if (type.equals(TeamRoleEnum.TEACHER.getValue())) {
            team.setTeamLeaderId(Long.valueOf(toUserId));
        }
        return this.updateById(team);
    }

    @Override
    public boolean teamCancelCaptain(TeamTransferOfIdentityRequest teamTransferOfIdentityRequest, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        Long teamId = teamTransferOfIdentityRequest.getTeamId();
        String type = teamTransferOfIdentityRequest.getType();
        String toUserId = teamTransferOfIdentityRequest.getToUserId();
        Long aLong = Long.valueOf(toUserId);
        boolean admin = userService.isAdmin(loginUser);
        ThrowUtils.throwIf(loginUser.getId().equals(aLong) && !admin, ErrorCode.OPERATION_ERROR, "不能取消自己的权限");
        Team team = this.getById(teamId);
        ThrowUtils.throwIf(team == null, ErrorCode.OPERATION_ERROR, "队伍不存在");
        if (!admin && !team.getTeamLeaderId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        ThrowUtils.throwIf(!team.getUsersId().contains(toUserId), ErrorCode.OPERATION_ERROR, "该用户不在队伍");
        Long captainId = team.getTeamCaptainId();
        if (!captainId.equals(aLong)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "取消用户身份不是队长");
        }
        if (admin || captainId.equals(loginUser.getId())) {
            team.setTeamCaptainId(team.getTeamLeaderId());
        }
        return this.updateById(team);
    }

    /**
     * 踢出队
     *
     * @param teamKickOutRequest 退出团队请求
     * @param request            要求
     * @return boolean
     */
    @Override
    public boolean kickOutTeam(TeamKickOutRequest teamKickOutRequest, HttpServletRequest request) {
        Long teamId = teamKickOutRequest.getTeamId();
        return redissonLockUtil.redissonDistributedLocks("saiyou:kickOutTeam:" + teamId, () -> {
            Long userId = teamKickOutRequest.getUserId();
            Team team = this.getById(teamId);
            if (team == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍私有不存在");
            }
            User loginUser = userService.getLoginUser(request);
            ThrowUtils.throwIf(loginUser.getId().equals(userId), ErrorCode.OPERATION_ERROR, "不能踢出给自己");
            if (!userService.isAdmin(loginUser) && !team.getTeamLeaderId().equals(loginUser.getId()) && !team.getTeamCaptainId().equals(loginUser.getId())) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
            ThrowUtils.throwIf(!team.getUsersId().contains(String.valueOf(userId)), ErrorCode.OPERATION_ERROR, "该用户不在队伍");
            User kickUser = userService.getById(userId);
            ThrowUtils.throwIf(kickUser == null, ErrorCode.OPERATION_ERROR, "踢出用户不存在");
            if (kickUser.getId().equals(team.getTeamCaptainId())) {
                team.setTeamCaptainId(loginUser.getId());
            }
            if (kickUser.getId().equals(team.getTeamLeaderId())) {
                team.setTeamLeaderId(loginUser.getId());
            }
            // 队伍中所有用户id
            List<Long> teamUserIdList = JSONUtil.toList(team.getUsersId(), Long.class);
            teamUserIdList.remove(userId);
            team.setUsersId(JSONUtil.toJsonStr(teamUserIdList));
            if (teamUserIdList.size()<=0){
                return this.removeById(team);
            }
            return this.updateById(team);
        });
    }

    /**
     * 加入团队
     *
     * @param joinTeam  加入团队
     * @param loginUser 登录用户
     * @return boolean
     */
    @Override
    public boolean joinTeam(TeamJoinRequest joinTeam, User loginUser) {
        Team team = this.getById(joinTeam.getTeamId());
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍私有不存在");
        }
        // 队伍密码加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + joinTeam.getPassword()).getBytes(StandardCharsets.UTF_8));
        // 当前队伍是加密队伍
        // 不是管理员需要密码
        if (!userService.isAdmin(loginUser) && team.getTeamStatus() == PRIVATE_TEAM_STATUS) {
            if (StringUtils.isBlank(joinTeam.getPassword()) || !encryptPassword.equals(team.getTeamPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }
        return redissonLockUtil.redissonDistributedLocks("saiyou:joinTeam:" + loginUser.getUserAccount(), () -> {
            // 当前队伍加入的队员id
            String usersId = team.getUsersId();
            List<Long> joinTeamUserIdList = JSONUtil.toList(usersId, Long.class);
            // 当前队伍是不是已经满人了
            // 可以补位两个人
            if (joinTeamUserIdList.size() >= team.getMaxNum()) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "当前队伍人数已满");
            }
            // 当前用户已经加入的队伍
            LambdaQueryWrapper<Team> teamLambdaQueryWrapper = new LambdaQueryWrapper<>();
            teamLambdaQueryWrapper.like(Team::getUsersId, loginUser.getId());
            List<Team> loginUserJoinTeamList = this.list();

            // 最多加入5个队伍
            if (loginUserJoinTeamList.size() >= 5) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多加入5个队伍");
            }
            Long currentCompetitionId = team.getCurrentCompetitionId();
            if (ObjectUtils.isNotEmpty(currentCompetitionId) && currentCompetitionId > 0) {
                Competition competition = competitionService.getById(currentCompetitionId);
                ThrowUtils.throwIf(competition == null, ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
                String signUpDeadline = competition.getSignUpDeadline();
                // 获取当前时间
                LocalDateTime currentTime = LocalDateTime.now();
                // 定义时间格式化器
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 将报名截止时间字符串按照指定格式解析为LocalDateTime对象
                LocalDateTime deadline = LocalDateTime.parse(signUpDeadline, formatter);
                // 判断当前时间是否在报名截止时间之后
                ThrowUtils.throwIf(currentTime.isAfter(deadline), ErrorCode.NOT_FOUND_ERROR, "当前队伍参加竞赛已开始,暂不可加入队伍");
            }
            // 是否已经加入该队伍
            if (joinTeamUserIdList.contains(loginUser.getId())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "已经加入过当前队伍");
            }
            // 加入队伍
            joinTeamUserIdList.add(loginUser.getId());
            team.setUsersId(JSONUtil.toJsonStr(joinTeamUserIdList));
            return this.updateById(team);
        });
    }


    @Override
    public boolean quitTeam(Long teamId, HttpServletRequest request) {
        return redissonLockUtil.redissonDistributedLocks("saiyou:quitTeam:" + teamId, () -> {
            User loginUser = userService.getLoginUser(request);
            Team team = this.getById(teamId);
            if (team == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
            }
            if (team.getTeamLeaderId().equals(loginUser.getId())) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "队长不能退出队伍");
            }
            if (team.getTeamCaptainId().equals(loginUser.getId())) {
                team.setTeamCaptainId(null);
            }
            List<Long> teamUserIdList = JSONUtil.toList(team.getUsersId(), Long.class);
            teamUserIdList.remove(loginUser.getId());
            team.setUsersId(JSONUtil.toJsonStr(teamUserIdList));
            return this.updateById(team);
        });
    }
}




